#include "Server.h"

#include "Configure.h"
#include "Util.h"
#include "dataShare/include/ApplicationLayer.h"
#include "iec61850_common.h"
#include "iec61850_model.h"
#include "iec61850_server.h"
#include "mms_value.h"
#include "static_model.h"

#include <utility>

int       Server::running   = 0;
IedServer Server::iedServer = NULL;

std::shared_ptr<ApplicationLayer> Server::appLayer = nullptr;

Server::Server() { appLayer = ApplicationLayer::GetInstance(); }

Server::~Server() {
    IedServer_stop(iedServer);
    IedServer_destroy(iedServer);
}

void Server::Init() {

    IedServerConfig config = IedServerConfig_create();
    iedServer = IedServer_createWithConfig(&iedModel, NULL, config);
    IedServerConfig_destroy(config);

    this->Config();
}

void Server::Start() {
    zlog_i("Server Start");
    IedServer_start(iedServer, 102);
    if (!IedServer_isRunning(iedServer)) {
        running = 0;
        zlog_e("Starting server failed! Exit.\n");
        IedServer_destroy(iedServer);
        exit(-1);
    }

    running = 1;
    zlog_i("IED Server start success");

    this->WriteHandler();
    this->ControlHandler();
}

void Server::Config() {
    WriteAccessPolicy();

    // 设置Goose接口
    IedServer_setGooseInterfaceId(iedServer, INTERFACE_1);
    IedServer_setAuxGooseInterfaceId(iedServer, INTERFACE_2);
    IedServer_setGoCBHandler(iedServer, goCbEventHandler, NULL);

    // 这个一定要放在最后
    IedServer_enableGoosePublishing(iedServer);
    zlog_i("enable goose publishing");
}

void Server::goCbEventHandler(MmsGooseControlBlock goCb,
                              int                  event,
                              void                *parameter) {
    zlog_i("Access to GoCB: %s\n", MmsGooseControlBlock_getName(goCb));
    zlog_i("         GoEna: %i\n", MmsGooseControlBlock_getGoEna(goCb));

    zlog_i("         DataSet: %s\n",
           MmsGooseControlBlock_getDataSet(goCb)->name);

    zlog_i("LogicalNode Name: %s",
           MmsGooseControlBlock_getLogicalNode(goCb)->name);

    zlog_i("event: %d", event);
}

void Server::ConnectionHandler(IedServer        self,
                               ClientConnection connection,
                               bool             connected,
                               void            *parameter) {

    const char *peerName = ClientConnection_getPeerAddress(connection);
    if (connected) {
        zlog_i("%s Connected", peerName);
    }
    else {
        printf("Connection closed\n");
        zlog_i("%s Disconnected", peerName);
    }
}

void Server::SigintHandler(int sig) { Server::running = 0; }

MmsDataAccessError Server::WriteAccessHandler(DataAttribute   *dataAttribute,
                                              MmsValue        *value,
                                              ClientConnection connection,
                                              void            *parameter) {

    if (dataAttribute ==
        IEDMODEL_ADU60ILLTONCU_GGIO5_Write_setOutputPower_setMag_f) {
        zlog_i("dataAttribute->name: %s", dataAttribute->name);
        if (MmsValue_getType(value) == MMS_FLOAT) {
            zlog_i("设置下发PCS功率：%f", MmsValue_toFloat(value));
        }
        else if (MmsValue_getType(value) == MMS_INTEGER) {
            zlog_i("设置下发PCS功率：%d", MmsValue_toInt32(value));
        }
    }

    zlog_w("dataAttribute->type: %d", dataAttribute->type);

    if (dataAttribute->type == IEC61850_BOOLEAN) {
        bool newValue = MmsValue_getBoolean(value);
        zlog_i("%s 写入布尔值 %s",
               dataAttribute->name,
               newValue ? "true" : "false");
    }

    if (dataAttribute->type == IEC61850_INT8 ||
        dataAttribute->type == IEC61850_INT16 ||
        dataAttribute->type == IEC61850_INT32 ||
        dataAttribute->type == IEC61850_ENUMERATED) {
        int newValue = MmsValue_toInt32(value);
        zlog_i("%s 写入整型 %d", dataAttribute->name, newValue);
    }
    else if (dataAttribute->type == IEC61850_FLOAT32) {
        float newValue = MmsValue_toFloat(value);
        zlog_i("%s 写入浮点数成功: %f", dataAttribute->name, newValue);

        /* Check if value is inside of valid range */
        // if ((newValue >= 0.f) && (newValue <= 1000.1f)) {
        //     zlog_i("%s 写入浮点数成功: %f", dataAttribute->name, newValue);
        //     return DATA_ACCESS_ERROR_SUCCESS;
        // }
        // else {
        //     zlog_i("写入浮点数失败超限");
        //     return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
        // }
    }

    std::string varName = Util::GetVarName((ModelNode *)dataAttribute);
    if (varName.empty()) {
        zlog_e("varName is empty");
        return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
    }
    std::vector<std::pair<std::string, std::string>> ctlrCmd;
    ctlrCmd.push_back(
        std::make_pair(varName, std::to_string(MmsValue_toFloat(value))));

    appLayer->SendControlData("CCU1", ctlrCmd);

    return DATA_ACCESS_ERROR_SUCCESS;
}

void Server::ControlHandler() {
    // NOTE: ControlHandler 接口
    SetControlHandler(IEDMODEL_ADU60ILLTONCU_GGIO4_Ctrl_runStop);
    // SetControlHandler(IEDMODEL_ADU60ILLTONCU_GGIO4_Ctrl_LCabMaxPowerRef);
}

ControlHandlerResult Server::ControlHandler(ControlAction action,
                                            void         *parameter,
                                            MmsValue     *value,
                                            bool          test) {

    // Control 写入的数据是Oper.ctlVal,  val = Oper.ctlVal
    // 模拟情况：需要将ctlVal的值写入到stVal中
    // 实际情况：需要将ctlVal的值下发到设备，然后将设备的状态写入到stVal中
    zlog_w("\033[32;49;1m < ControlHandler: %s > \033[39;49;0m",
           ControlAction_getControlObject(action)->name);

    if (test) {
        zlog_w("ControlHandler receive test");
        return CONTROL_RESULT_FAILED;
    }

    DataObject *object = ControlAction_getControlObject(action);

    DataAttribute *stVal =
        (DataAttribute *)ModelNode_getChild((ModelNode *)object, "stVal");
    DataAttribute *t =
        (DataAttribute *)ModelNode_getChild((ModelNode *)object, "t");
    DataAttribute *q =
        (DataAttribute *)ModelNode_getChild((ModelNode *)object, "q");

    zlog_i("%s, ControlHandler, mmsValue type:  %d, attr type: %d : %s",
           object->name,
           MmsValue_getType(value),
           MmsValue_getType(stVal->mmsValue),
           MmsValue_getBoolean(value) ? "true" : "false");

    // if (object == IEDMODEL_PCS_ZINV1_RunStop) {
    //
    //     zlog_i("%s, type:  %d, PCS %s",
    //            object->name,
    //            MmsValue_getType(value),
    //            MmsValue_getBoolean(value) ? "Start" : "Stop");
    // }

    uint64_t timeStamp = Hal_getTimeInMs();

    if (stVal != NULL) {
        zlog_w("\033[32;49;1m < update stVal : %s > \033[39;49;0m",
               stVal->name);
        IedServer_updateAttributeValue(iedServer, stVal, value);
    }
    if (t != NULL) {
        IedServer_updateUTCTimeAttributeValue(iedServer, t, timeStamp);
    }
    if (q != NULL) {
        IedServer_updateQuality(iedServer, q, QUALITY_VALIDITY_GOOD);
    }

    // else {
    //     return CONTROL_RESULT_FAILED;
    // }

    //NOTE: 模拟解析部分

    return CONTROL_RESULT_OK;
}

void Server::SetControlHandler(DataObject *object) {

    IedServer_setControlHandler(
        this->iedServer, object, ControlHandler, object);
}

void Server::WriteAccessPolicy() {
    IedServer_setWriteAccessPolicy(
        Server::iedServer, IEC61850_FC_DC, ACCESS_POLICY_ALLOW);

    IedServer_setWriteAccessPolicy(
        Server::iedServer, IEC61850_FC_ST, ACCESS_POLICY_ALLOW);

    IedServer_setWriteAccessPolicy(
        Server::iedServer, IEC61850_FC_CF, ACCESS_POLICY_ALLOW);

    IedServer_setWriteAccessPolicy(
        Server::iedServer, IEC61850_FC_GO, ACCESS_POLICY_ALLOW);
}

void Server::HandleWriteAccess(DataAttribute *attr) {
    // IedServer_handleWriteAccess(
    //     this->iedServer, attr, WriteAccessHandler, NULL);

    IedServer_handleWriteAccessForComplexAttribute(
        this->iedServer, attr, WriteAccessHandler, NULL);
}

void Server::Lock() { IedServer_lockDataModel(iedServer); }

void Server::Unlock() { IedServer_unlockDataModel(iedServer); }

void Server::WriteHandler() {
    // NOTE: write 接口

    HandleWriteAccess(IEDMODEL_ADU60ILLTONCU_GGIO5_Write_setOutputPower_setMag);
}
